/*    /secure/cmds/player/bug.c
 *    from the Dead Soulsr1 Object Library
 *    the command interface to the NM IV bug tracking system
 *    created by Descartes of Borg 950925
 */

#include <lib.h>
#include <daemons.h>
#include <message_class.h>

inherit LIB_DAEMON;

void PreMenu(string str);
static varargs void MainMenu(string str);
static void Assign(string *args);
static void EndAssign(string *args);
static void Complete(string *args);
void EndComplete(int x);
static void Delete(string *args);
static void Report(string *args);
static void EndReport(string type, string data, string file);
varargs static void View(string *args, int print);
static string GetBugString(int id, mapping bugs);

static void create() {
    daemon::create();
    SetNoClean(1);
}

mixed cmd(string str) {
    string *args;
    int i;

    if( !str || str == "" ) args = ({});
    else args = explode(str, " ");
    if( !(i = sizeof(args)) ) MainMenu();
    else {
        string opt;

        opt = args[0];
        if( i == 1 ) args = ({});
        else args = args[1..];
        switch(opt) {
        case "-a": Assign(args); break;
        case "-c": Complete(args); break;
        case "-d": Delete(args); break;
        case "-r": Report(args); break;
        case "-p": View(args, 1); break;
        case "-v": View(args); break;
        default: return "To report a bug, use \"bug -r\".";
        }
    }
    return 1;
}

void PreMenu(string str) { 
    if( str == "q" ) {
        message("system", "Exiting the bug tracking system.", this_player());
        return;
    }
    MainMenu(); 
}

varargs static void MainMenu(string str) {
    string tmp;
    int cols;

    if( str && str != "" ) {
        switch(str) {
        case "a": Assign(({})); return;
        case "c": Complete(({})); return;
        case "d": Delete(({})); return;
        case "r": Report(({})); return;
        case "p": View(({}), 1); return;
        case "v": View(({})); return;
        case "q": 
            message("system", "Exiting the bug tracking system.", 
              this_player());
            return;
        }
    }
    cols = ((int *)this_player()->GetScreen())[0] || 80;
    tmp = center("Dead Souls Bug Tracking System", cols) + "\n\n";
    if( creatorp(this_player()) ) {
        tmp += "a)ssign bug to creator\n";
        tmp += "c)omplete work on a bug\n";
    }
    if( archp(this_player()) ) tmp += "d)elete a bug from the system\n";
    tmp += "r)eport a new bug to the system\n";
    tmp += "v)iew an existing bug or a list of existing bugs\n";
    tmp += "\nq)uit the bug tracking system\n";
    message("system", tmp, this_player());
    message("prompt", "Enter your choice: ", this_player());
    input_to( (: MainMenu :) );
}

static void Assign(string *args) {
    int i;

    if( !creatorp(this_player()) ) {
        message("system", "Only creators may use the -a option.",
          this_player());
        return;
    }
    if( !(i = sizeof(args)) ) {
        message("prompt", "Enter in the bug ID: ", this_player());
        input_to(function(string str) { Assign(({ str })); });
          return;
      }
        else if( i == 1 ) {
            int x;

            if( (x = to_int(args[0])) > 0 ) { /* assume a bug id for now */
                if( !archp(this_player()) ) 
                    Assign( ({ args[0], (string)this_player()->GetCapName() }) );
                else {
                    message("prompt", "Enter the creator to assign it to [" +
                      (string)this_player()->GetCapName() + "]: ",
                      this_player());
                    input_to(function(string str, string id) {
                          if( !str || str == "" ) 
                              str = (string)this_player()->GetCapName();
                          Assign( ({ str, id }) );
                      }, args[0]);
                }
                return;
            }
            else {
                message("prompt", "Enter in the bug ID to assign to " +
                  capitalize(args[0]) + ": ", this_player());
                input_to(function(string id, string str) { 
                      Assign( ({ id, str }) );
                  }, args[0]);
                return;
            }
        }
        else if( i == 2 ) {
            message("prompt", "Do you wish to comment? [n]: ", this_player());
            input_to(function(string str, string *args) {
                  if( !str || str == "" ) str = "n";
                  else str = lower_case(str[0..0]);
                  if( str == "y" ) {
                      string file;

                      message("system", "Enter comments on the bug...", 
                        this_player());
                      file = DIR_TMP "/" + (string)this_player()->GetKeyName();
                      rm(file);
                      this_player()->eventEdit(file, (: EndAssign, args :));
                      return;
                  }
                  else Assign(args + ({ "" }));
              }, args);
            return;
        }
        else {
            string who, comments;
            int x;

            if( (x = to_int(args[0])) < 1 ) {
                who = args[0];
                if( (x = to_int(args[1])) < 1 ) {
                    message("system", "Invalid bug ID " + x + ".", this_player());
                    message("prompt", "Hit return: ", this_player());
                    input_to( (: PreMenu :) );
                    return;
                }
            }
            else who = args[1];
            comments = args[2];
            if( !archp(this_player()) && (convert_name(who) !=
                (string)this_player()->GetKeyName()) ) {
                message("system", "Only arches may assign bugs to other people.",
                  this_player());
                message("prompt", "Hit return: ", this_player());
                input_to( (: PreMenu :) );
                return;
            }
            if( !user_exists(convert_name(who)) ) {
                message("system", "No such creator: " + who, this_player());
                message("prompt", "Hit return: ", this_player());
                input_to( (: PreMenu :) );
                return;
            }
            if( !((int)BUGS_D->eventAssign(x, who)) ) {
                message("system", "Failed to assign bug.", this_player());
                return;
            }
            if( comments != "" ) BUGS_D->AddComment(x, comments);
            message("system", "Assigned bug to " + who + ".", this_player());
            return;
        }
    }

    static void EndAssign(string *args) {
        string file, contents;

        file = DIR_TMP "/" + (string)this_player()->GetKeyName();
        contents = (read_file(file) || "");
        rm(file);
        Assign(args + ({ contents }));
    }

    static void Complete(string *args) {
        string file;
        int x;

        if( !sizeof(args) ) {
            message("prompt", "Enter the bug ID: ", this_player());
            input_to(function(string str) { Complete( ({ str }) ); });
              return;
          }
            else if( !creatorp(this_player()) ) {
                message("system", "Invalid command.", this_player());
                message("prompt", "Hit return: ", this_player());
                input_to( (: PreMenu :) );
                return;
            }	
            else if( (x = to_int(args[0])) < 1 ) {
                message("system", "Invalid bug ID.", this_player());
                message("prompt", "Hit return: ", this_player());
                input_to( (: PreMenu :) );
                return;
            }
            message("system", "Enter in your comments:", this_player());
            file = DIR_TMP "/" + (string)this_player()->GetKeyName();
            if( file_exists(file) ) rm(file);
            this_player()->eventEdit(file, (: EndComplete, x :));
        }

        void EndComplete(int x) {
            string file, stuff;

            if( previous_object() != this_player(1) ) return;
            file = DIR_TMP "/" + (string)this_player()->GetKeyName();
            if( !(stuff = read_file(file)) ) {
                message("system", "Edit aborted.", this_player());
                rm(file);
                return;
            }
            rm(file);
            if( !((int)BUGS_D->eventComplete(x, stuff)) ) {
                message("system", "Failed to set the bug completed.", this_player());
                return;
            }
            message("system", "Bug marked completed!", this_player());
        }

        static void Delete(string *args) {
            if( !archp(this_player()) ) {
                message("system","You must be an arch to delete bugs.", this_player());
                return;
            }
            if( !sizeof(args) ) {
                message("prompt", "Delete which bug? ", this_player());
                input_to(function(string str) { Delete( ({ str }) ); });
                  return;
              }
                else {
                    int x;

                    if( (x = to_int(args[0])) < 1 ) 
                        message("system", "Invalid bug ID.", this_player());
                    else if( !((int)BUGS_D->eventDelete(x)) ) 
                        message("system", "Delete failed.", this_player());
                    else message("system", "Deletion succeeded.", this_player());
                    return;
                }
            }

            static void Report(string *args) {
                if( archp(this_player()) && sizeof(args) ) {
                    string data;
                    string bug;
                    int x;

                    data = "Room: " + file_name(environment(this_player()));
                    bug = implode(args, " ");
                    if( x = (int)BUGS_D->eventReport((string)this_player()->GetCapName(), 
                        "approval", bug, data) ) {
                        BUGS_D->eventAssign(x, query_privs(environment(this_player())));
                        message("system", "Bug reported.", this_player());
                        return;
                    }
                    else {
                        message("system", "Error in reporting bug.", this_player());
                        return;
                    }
                }
                else EndReport(0, "Room: " + file_name(environment(this_player())), 0);
            }

            static void EndReport(string type, string data, string file) {
                string tmp;
                int x;

                if( !type ) {
                    message("system", "Choose a bug type from among the following:\n",
                      this_player());
                    message("system", "\tidea (some nifty idea to add to the game)",
                      this_player());
                    message("system", "\ttypo (misspelling, lexigraphical weirdness)",
                      this_player());
                    message("system", "\tunexplained behaviour (something "
                      "contrary to how you would expect it)", this_player());
                    message("system", "\truntime (one of those nasty error messages)\n",
                      this_player());
                    message("system", "\tother\n", this_player());
                    message("prompt", "Enter type: ", this_player());
                    input_to( (: EndReport :), data, 0);
                    return;
                }
                if( !file ) {
                    file = DIR_TMP "/" + (string)this_player()->GetKeyName();
                    rm(file);
                    message("system", "Enter in a description of the bug.  When done, "
                      "enter a period on a line by itself.", this_player());
                    this_player()->eventEdit(file, (: EndReport, type, data, file :));
                    return;
                }
                if( !(tmp = read_file(file)) ) {
                    message("system", "Bug report aborted.", this_player());
                    rm(file);
                    return;
                }
                rm(file);
                if( type == "runtime" ) {
                    mapping last_error;

                    if( last_error = (mapping)this_player()->GetLastError() )
                        data += "\n" + (string)master()->standard_trace(last_error) + "\n";
                }
                if( !(x = (int)BUGS_D->eventReport((string)this_player()->GetCapName(),
                      type, tmp, data)) ) {
                    message("system", "Bug report failed.", this_player());
                    return;
                }
                message("system", "Bug reported, thank you!  Your tracking id is " +
                  x + ".", this_player());
            }

            varargs static void View(string *args, int print) {
                mapping bugs;
                function f;

                f = function() { 
                    message("prompt", "\nHit return: ", this_player());
                    input_to( (: PreMenu :) );
                };
                if( !sizeof(args) ) {
                    message("system", "View:\n\t1) all bugs\n"
                      "\t2) unassigned bugs only\n"
                      "\t3) assigned bugs only\n"
                      "\t4) completed bugs only\n", this_player());
                    if( creatorp(this_player()) )
                        message("prompt", "Enter a choice [3]: " , this_player());
                    else message("prompt", "Enter a choice [1]: ", this_player());
                    input_to(function(string str, string it_sucks, int print) {
                          if( !str || str == "" ) {
                              if( creatorp(this_player()) ) str = "3";
                              else str = "1";
                          }
                          if( str < "1" || str > "4" ) {
                              message("system", "Invalid selection", this_player());
                              message("prompt", "Hit return: ", this_player());
                              input_to( (: PreMenu :) );
                              return;
                          }
                          View( ({ str }), print );
                      }, "", print);
                    return;
                }
                else if( sizeof(args) == 1 && !creatorp(this_player()) ) {
                    mapping bug;
                    string tmp = "";
                    int bug_id;

                    bugs = (mapping)BUGS_D->GetBugs();
                    foreach( bug_id, bug in bugs ) {
                        if( bug["who"] != (string)this_player()->GetCapName() )
                            continue;
                        if( args[0] == "1" || (args[0] == "2" && !bug["assigned"]) ||
                          (args[0] == "3" && bug["assigned"] && !bug["date fixed"]) ||
                          (args[0] == "4" && bug["date fixed"]) )
                            tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
                    }
                    if( tmp == "" ) {
                        message("system", "No bugs meet your query criteria.", 
                          this_player());
                        message("prompt", "Hit return: ", this_player());
                        input_to( (: PreMenu :) );
                        return;
                    }
                    this_player()->eventPage(explode(tmp, "\n"), MSG_SYSTEM, f);
                    return;
                }
                else if( sizeof(args) == 1 ) {
                    message("system", "View:\n\t1) all bugs\n"
                      "\t2) bugs assigned to me\n"
                      "\t3) bugs reported by me\n", this_player());
                    message("prompt", "Enter choice [2]: ", this_player());
                    input_to(function(string str, string one, int print) {
                          if( !str || str == "" ) str = "2";
                          else if( str < "1" || str > "3" ) {
                              message("system", "Invalid selection.", this_player());
                              message("prompt", "Hit return: ", this_player());
                              input_to( (: PreMenu :) );
                              return;
                          }
                          View( ({ one, str }), print );
                      }, args[0], print);
                    return;
                }
                else {
                    mapping bug;
                    string nom, tmp = "";
                    int bug_id;

                    nom = (string)this_player()->GetKeyName();
                    bugs = (mapping)BUGS_D->GetBugs();
                    if( !creatorp(this_player()) ) {
                        View( ({ args[0] }), print );
                        return;
                    }
                    else foreach(bug_id, bug in bugs) {
                        string opt1, opt2;

                        opt1 = args[0];
                        opt2 = args[1];
                        if( opt1 == "1" && opt2 == "1" ) 
                            tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
                        else {
                            if( opt2 == "2" && (!bug["assigned"] || 
                                convert_name(bug["assigned"]) != nom) ) 
                                continue;
                            else if(opt2 == "3" && convert_name(bug["who"]) != nom )
                                continue;
                            if( opt1 == "2" && !bug["assigned"] )
                                tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
                            else if( opt1 == "3" && bug["assigned"] && !bug["date fixed"] )
                                tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
                            else if( opt1 == "4" && bug["date fixed"] )
                                tmp += GetBugString(bug_id, bugs) + "\n*****\n\n";
                        }
                    }
                    if( tmp == "" ) {
                        message("system", "No bugs match your query.", this_player());
                        message("prompt", "Hit return: ", this_player());
                        input_to( (: PreMenu :) );
                        return;
                    }
                    if( print && creatorp(this_player()) ) {
                        string file;

                        rm(file = user_path((string)this_player()->GetKeyName()) + "bugs");
                        write_file(file, strip_colours(tmp));
                    }
                    else this_player()->eventPage(explode(tmp, "\n"), MSG_SYSTEM, f);
                    return;
                }
            }

            static string GetBugString(int id, mapping bugs) {
                string tmp;

                tmp = "%^YELLOW%^Bug ID:%^RESET%^ " + id + "\n";
                tmp += "%^YELLOW%^Reported by:%^RESET%^ " + 
                bugs[id]["who"] + "\n";
                if( bugs[id]["assigned"] ) {
                    tmp += "%^YELLOW%^Status: %^RESET%^";
                    if( !bugs[id]["date fixed"] ) 
                        tmp += "assigned to " + bugs[id]["assigned"] + "\n";
                    else tmp += "completed " + ctime(bugs[id]["date fixed"]) + "\n";
                }
                else tmp += "%^YELLOW%^Status:%^RESET%^ unassigned\n";
                tmp += "%^YELLOW%^Type:%^RESET%^ " + bugs[id]["type"] + "\n";
                if( bugs[id]["date fixed"] )
                    tmp += "%^YELLOW%^Notes:%^RESET%^\n" + bugs[id]["resolution"] + "\n";
                if( creatorp(this_player()) ) 
                    tmp += "\n%^YELLOW%^Creator info:%^RESET%^\n" + bugs[id]["data"] + "\n";
                tmp += "\n%^YELLOW%^Bug info:%^RESET%^\n" + bugs[id]["bug"] + "\n";
                return tmp;
            }

            string GetHelp(string str) {
                string tmp;

                tmp = "Syntax: <bug>\n";
                if( creatorp(this_player()) ) {
                    tmp += "        <bug -a (BUG_ID CREATOR>)\n";
                    tmp += "        <bug -c (BUG_ID)>\n";
                }
                if( archp(this_player()) ) tmp += "        <bug -d (BUG_ID)>\n";
                tmp += "        <bug -r>\n        <bug -v ([1-4] [1-3])>\n\n";
                tmp += "The command interface to the Dead Souls Bug Tracking System.  "
                "You can simply type \"bug\" and be prompted for further options, "
                "or, if you understand the system, pass command line arguments "
                "to the bug command to make things go faster.  This system allows "
                "players to report bugs or ideas and periodically see what has "
                "been done about their report.  It also allows creators a way "
                "to track bugs which have been reported to them and give feedback "
                "to the players who have reported them.  It gives admins a way to "
                "track and assign mudlib level bugs.  The options above correspond "
                "to assigning, completing, deleting, reporting, and viewing bugs "
                "respectively.\n\n"
                "See also: praise";
                return tmp;
            }

